package edu.umd.fosite.geom;

import edu.umd.fosite.data.FiniteLatticeDouble2D;
import edu.umd.fosite.data.LatticeDouble2D;

public class TriangleMeshSurface implements Surface {

	public LatticeDouble2D data;

	public TriangleMeshSurface(LatticeDouble2D data) {
		this.data = data;
	}

	@Override
	public double at(double x, double y) {
		int ix = (int) x, iy = (int) y;
		double rx = x - ix, ry = y - iy;
		double b = data.get(ix + 1, iy), c = data.get(ix, iy + 1);
		if (ry < 1 - rx) {
			double a = data.get(ix, iy);
			return (b - a) * rx + (c - a) * ry + a;
		} else {
			double a = data.get(ix + 1, iy + 1);
			return (b - a) * (1 - rx) + (c - a) * (1 - ry) + a;
		}
	}

	public static TriangleMeshSurface generateDiamondSquare(double rough) {
		final int dim = 129 * 2 - 1;
		FiniteLatticeDouble2D data = new FiniteLatticeDouble2D(dim, dim, 0);

		for (int iter = dim - 1; iter > 0; iter /= 2) {
			rough *= 0.9;
			for (int x = 0; x < dim - 1; x += iter)
				for (int y = 0; y < dim - 1; y += iter) {
					// Center point
					int cx = x + iter / 2, cy = y + iter / 2;
					double a = data.get(x, y);
					double b = data.get(x, y + iter);
					double c = data.get(x + iter, y);
					double d = data.get(x + iter, y + iter);

					double mag = rough * iter;
					data.set(cx, cy, (a + b + c + d) / 4
							+ (Math.random() - 0.5) * mag);

					double v = data.get(cx, cy);
					if (x == 0)
						data.set(cx - iter / 2, cy, (v + a + c) / 3);
					else
						data.set(cx - iter / 2, cy,
								(v + a + c + data.get(cx - iter, cy)) / 4);
					if (y == 0)
						data.set(cx, cy - iter / 2, (v + a + b) / 3);
					else
						data.set(cx, cy - iter / 2,
								(v + a + b + data.get(cx, cy - iter)) / 4);

					data.set(cx + iter / 2, cy, (v + b + d) / 3);
					data.set(cx, cy + iter / 2, (v + d + c) / 3);
				}
		}

		return new TriangleMeshSurface(data);
	}
}
